package com.xsy.glsurfaceview

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.opengl.GLES30
import android.opengl.GLUtils
import android.opengl.Matrix
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder


class TextureRenderBlur(bitmap: Bitmap, vertexShaderCode: String, fragmentShaderCode: String) {
    companion object {
        const val vertex_file_path = "vshader/TextureSketch.glsl"
        const val fragment_file_path = "fshader/TextureSketch.glsl"
        private const val KERNEL_SIZE = 9
    }


    private val coordData = floatArrayOf(
        //顶点坐标           纹理坐标
        -1.0f, 1.0f, 0.0f, 0.0f, 0.0f,  //左上角
        -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, //左下角
        1.0f, 1.0f, 0.0f, 1.0f, 0.0f,   //右上角
        1.0f, -1.0f, 0.0f, 1.0f, 1.0f   //右下角
    )


    private var translateMatrix = FloatArray(16)

    private var mProgram = -1
    private var mPositionHandle = -1
    private var mTexCoordHandle = -1
    private var mMVPTMatrixHandle = -1
    private var mSamplerHandle = -1

    private var mKernelHandle = -1
    private var mTexOffsetHandle = -1
    private var mColorOffsetHanle = -1
    private val mKernel = FloatArray(KERNEL_SIZE)
    private lateinit var mTexOffset: FloatArray
    private var mColorOffset = 0.0f

    private var mTextureId = -1

    private val vboId: IntArray = IntArray(1)
    private fun loadShader(type: Int, shaderCode: String): Int {
        val shader = GLES30.glCreateShader(type)
        GLES30.glShaderSource(shader, shaderCode)
        GLES30.glCompileShader(shader)
        return shader
    }

    private fun setKernel(kernel: FloatArray, colorOffset: Float) {
        System.arraycopy(kernel, 0, mKernel, 0, KERNEL_SIZE)
        mColorOffset = colorOffset
    }

    private fun setTexSize(width: Int, height: Int) {
        val tw = 1.0f / width
        val th = 1.0f / height

        mTexOffset = floatArrayOf(
            -tw, -th, 0f, -th, tw, -th,
            -tw, 0f, 0f, 0f, tw, 0f,
            -tw, th, 0f, th, tw, th
        )

    }

    init {
        mTextureId = uploadTexture(bitmap)
        val bb = ByteBuffer.allocateDirect((coordData.size * 4))
        bb.order(ByteOrder.nativeOrder())
        val coordBuffer = bb.asFloatBuffer()
        coordBuffer.put(coordData)
        coordBuffer.position(0)
        Log.d("MyGLSurfaceView", "fragment shader code: $fragmentShaderCode")
        Log.d("MyGLSurfaceView", "vertex shader code: $vertexShaderCode")
        val vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
        mProgram = GLES30.glCreateProgram()
        GLES30.glAttachShader(mProgram, vertexShader)
        GLES30.glAttachShader(mProgram, fragmentShader)
        GLES30.glLinkProgram(mProgram)
        GLES30.glDeleteShader(vertexShader)
        GLES30.glDeleteShader(fragmentShader)
        GLES30.glGenBuffers(1, vboId, 0)
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboId[0])
        GLES30.glBufferData(
            GLES30.GL_ARRAY_BUFFER, bb.capacity(),
            bb, GLES30.GL_STATIC_DRAW
        )
        Matrix.setIdentityM(translateMatrix, 0)
        mPositionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")
        GLES30.glEnableVertexAttribArray(mPositionHandle)
        checkGlError("aPosition")
        mTexCoordHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord")
        GLES30.glEnableVertexAttribArray(mTexCoordHandle)
        checkGlError("aTexCoord")
        mMVPTMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPTMatrix")
        checkGlError("uTMatrix")
        mSamplerHandle = GLES30.glGetUniformLocation(mProgram, "uSampler")
        checkGlError("uSampler")
        mKernelHandle = GLES30.glGetUniformLocation(mProgram, "uKernel")
        if (mKernelHandle < 0) {
            mTexOffsetHandle = -1
            mColorOffsetHanle = -1
            mKernelHandle = -1
        } else {
            mTexOffsetHandle = GLES30.glGetUniformLocation(mProgram, "uTexOffset")
            mColorOffsetHanle = GLES30.glGetUniformLocation(mProgram, "uColorOffset")
            //0.5 很亮 0 正常
            //模糊
//            setKernel(
//                floatArrayOf(
//                    1f / 16f,
//                    2f / 16f,
//                    1f / 16f,
//                    2f / 16f,
//                    4f / 16f,
//                    2f / 16,
//                    1f / 16f,
//                    2f / 16f,
//                    1f / 16f
//                ), 0.5f
//            )
//            setKernel(floatArrayOf(1f/9f, 1f/9f, 1f/9f, 1f/9f, 1f/9f, 1f/9, 1f/9f, 1f/9f, 1f/9f), 0f)
            //锐化
//            setKernel(floatArrayOf(0f, -1f, 0f, -1f, 5f, -1f, 0f, -1f, 0f), 0f)
//            setKernel(floatArrayOf(-1f, -1f, -1f, -1f, 9f, -1f, -1f, -1f, -1f), 0f)
            //边缘检测
//            setKernel(floatArrayOf(-1f, 0f, 1f, -2f, 0f, 2f, -1f, 0f, 1f), 0f)
//            setKernel(floatArrayOf(-1f, -1f, -1f, -1f, 8f, -1f, -1f, -1f, -1f), 0.5f)
            //浮雕效果
//            setKernel(floatArrayOf(2f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, -1f), 0.5f)
//            setKernel(floatArrayOf(-2f, -1f, 0f, -1f, 0f, 1f, 0f, 1f, 2f), 0.5f)
            setTexSize(512, 512)
        }
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
    }

    fun draw(mvpMatrix: FloatArray) {
        //使用program
        GLES30.glUseProgram(mProgram)
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboId[0])

        GLES30.glVertexAttribPointer(
            mPositionHandle,
            3,
            GLES30.GL_FLOAT,
            false,
            5 * Float.SIZE_BYTES,
            0
        )
        checkGlError("mPositionHandle")

        GLES30.glVertexAttribPointer(
            mTexCoordHandle,
            2,
            GLES30.GL_FLOAT,
            false,
            5 * Float.SIZE_BYTES,
            3 * Float.SIZE_BYTES
        )
        checkGlError("mTexCoordHandle")
        //传入正交矩阵修复变形
        GLES30.glUniformMatrix4fv(mMVPTMatrixHandle, 1, false, mvpMatrix, 0)
        checkGlError("mTMatrixHandle")

        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureId)
        GLES30.glUniform1i(mSamplerHandle, 0)
        if (mKernelHandle >= 0) {
            GLES30.glUniform1fv(mKernelHandle, KERNEL_SIZE, mKernel, 0)
            GLES30.glUniform2fv(mTexOffsetHandle, KERNEL_SIZE, mTexOffset, 0)
            GLES30.glUniform1f(mColorOffsetHanle, mColorOffset)
        }
        //drawAarray, 绘制矩型
        GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)

        // 解绑VBO
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
    }

    fun onDestroy() {
        GLES30.glDeleteProgram(mProgram)
        GLES30.glDeleteBuffers(1, vboId, 0)
    }


    private fun uploadTexture(bitmap: Bitmap): Int {

        val textureIds = IntArray(1)
        GLES30.glGenTextures(1, textureIds, 0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0])

        GLES30.glTexParameteri(
            GLES30.GL_TEXTURE_2D,
            GLES30.GL_TEXTURE_MIN_FILTER,
            GLES30.GL_LINEAR
        )
        GLES30.glTexParameteri(
            GLES30.GL_TEXTURE_2D,
            GLES30.GL_TEXTURE_MAG_FILTER,
            GLES30.GL_LINEAR
        )

        GLUtils.texImage2D(
            GLES30.GL_TEXTURE_2D,
            0,
            GLES30.GL_RGBA,
            bitmap,
            0
        )
        bitmap.recycle()
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
        return textureIds[0]
    }

    fun checkGlError(op: String) {
        val error = GLES30.glGetError();

        if (error != GLES30.GL_NO_ERROR) {
            val msg = op + ": glError 0x" + Integer.toHexString(error)
            Log.e("MyGLSurfaceView", msg)
        }
    }


}